﻿using System;
using Microsoft.Xna.Framework;
using System.IO;

namespace ADiKt {
    public enum ScreenState {TransitionOn, Active, TransitionOff, Hidden}

    ///<summary>
    /// A single layer that has update, draw and input handeling logic,
    /// as well as an idea of focus and transitiion on/off states.
    /// split into normal screens and popups, for popups screens underneath
    /// do not transition off. The main menu responds to input from any 
    /// connected gamepad, but whichever player makes a selection from
    /// this menu is given control over all subsequent screens
    /// </summary>
    public abstract class Screen {

        ///<summary> Indicates whether the screen is a popup </summary>
        public bool IsPopup {
            get { return isPopup; }
            protected set { isPopup = value; }
        }
        bool isPopup = false;

        ///<summary>gets/sets the transition on time </summary>
        public TimeSpan TransitionOnTime {
            get { return transitionOnTime; }
            protected set { transitionOnTime = value; }
        }
        TimeSpan transitionOnTime = TimeSpan.Zero;

        ///<summary>gets/sets the transition off time </summary>
        public TimeSpan TransitionOffTime {
            get { return transitionOffTime; }
            protected set { transitionOffTime = value; }
        }
        TimeSpan transitionOffTime = TimeSpan.Zero;

        /// <summary> Gets the current position of the screen transition [from 0=on to 1=off]</summary>
        public float TransitionPosition {
            get { return transitionPosition; }
            protected set { transitionPosition = value; }
        }
        float transitionPosition = 1;

        /// <summary> Gets the current alpha of the screen transition, [from 1=on to 0=off] </summary>
        public float TransitionAlpha {
            get { return 1f - TransitionPosition; }
        }

        /// <summary> Gets the current screen transition state. </summary>
        public ScreenState ScreenState {
            get { return screenState; }
            protected set { screenState = value; }
        }
        ScreenState screenState = ScreenState.TransitionOn;

        /// <summary> Indicates whether screen is bieing removed or just hidden underneth another screen </summary>
        public bool IsExiting {
            get { return isExiting; }
            protected internal set { isExiting = value; }
        }
        bool isExiting = false;

        /// <summary> Checks whether this screen is active and can respond to user input. </summary>
        public bool IsActive {
            get {return !otherScreenHasFocus && (screenState == ScreenState.TransitionOn || screenState == ScreenState.Active);}
        }
        bool otherScreenHasFocus;

        /// <summary> Gets the screen manager this screen is part of </summary>
        public ScreenManager ScreenManager {
            get { return screenManager; }
            internal set { screenManager = value; }
        }
        ScreenManager screenManager;

        /// <summary> Gets the index of the player who is currently controlling this screen </summary>
        public PlayerIndex? ControllingPlayer {
            get { return controllingPlayer; }
            internal set { controllingPlayer = value; }
        }
        PlayerIndex? controllingPlayer;


        /// <summary> Load content for the screen.</summary>
        public virtual void LoadContent() { }

        /// <summary> Unload content for the screen.</summary>
        public virtual void UnloadContent() { }

        /// <summary> Allows the screen to run logic, such as updating the transition position.
        /// Called regardless te screens state. </summary>
        public virtual void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen) {
            this.otherScreenHasFocus = otherScreenHasFocus;

            if (isExiting) {
                // If the screen is going away to die, it should transition off.
                screenState = ScreenState.TransitionOff;

                if (!UpdateTransition(gameTime, transitionOffTime, 1)) {
                    // When the transition finishes, remove the screen.
                    ScreenManager.RemoveScreen(this);
                }
            } else if (coveredByOtherScreen) {
                // If the screen is covered by another, it should transition off.
                if (UpdateTransition(gameTime, transitionOffTime, 1)) {
                    // Still busy transitioning.
                    screenState = ScreenState.TransitionOff;
                } else {
                    // Transition finished
                    screenState = ScreenState.Hidden;
                }
            } else {
                // Otherwise the screen should transition on and become active.
                if (UpdateTransition(gameTime, transitionOnTime, -1)) {
                    // Still busy transitioning.
                    screenState = ScreenState.TransitionOn;
                } else {
                    // Transition finished
                    screenState = ScreenState.Active;
                }
            }
        }

        /// <summary> Helper for updating the screen transition position.</summary>
        bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction) {
            // How much should we move by?
            float transitionDelta = (time == TimeSpan.Zero) ? 1 : (float)(gameTime.ElapsedGameTime.TotalMilliseconds / time.TotalMilliseconds);
            transitionPosition += transitionDelta * direction;

            // Did we reach the end of the transition?
            if (((direction < 0) && (transitionPosition <= 0)) || ((direction > 0) && (transitionPosition >= 1))) {
                transitionPosition = MathHelper.Clamp(transitionPosition, 0, 1);
                return false;
            }
            // Otherwise we are still busy transitioning.
            return true;
        }

        /// <summary> Allows the screen to handle user input, only called when the screen is active </summary>
        public virtual void HandleInput(InputState input) { }

        /// <summary> This is called when the screen should draw itself. </summary>
        public virtual void Draw(GameTime gameTime) { }

        /// <summary>
        /// Tells the screen to go away. Unlike ScreenManager.RemoveScreen, which
        /// instantly kills the screen, this method respects the transition timings
        /// and will give the screen a chance to gradually transition off.
        /// </summary>
        public void ExitScreen() {
            // If transition time=0, remove it immediately, else flag that it should transition off & exit.
            if (TransitionOffTime == TimeSpan.Zero) {
                ScreenManager.RemoveScreen(this);
            } else {
                isExiting = true;
            }
        }

    }
}
